package com.nowcoder.topic.pointers.middle;

/**
 * NC202 长度最小的连续子数组
 * @author d3y1
 */
public class NC202{
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @param target int整型
     * @return int整型
     */
    public int minSubarray (int[] nums, int target) {
        return solution1(nums, target);
        // return solution2(nums, target);
        // return solution3(nums, target);
        // return solution4(nums, target);
    }

    /**
     * 前缀和 + 双指针(滑动窗口)
     * @param nums
     * @param target
     * @return
     */
    private int solution1(int[] nums, int target){
        int n = nums.length;

        // 前缀和: pre[i]表示前i项的和
        int[] pre = new int[n+1];
        int val;
        for(int i=1; i<=n; i++){
            val = nums[i-1];
            if(val >= target){
                return 1;
            }
            pre[i] = pre[i-1]+val;
        }
        if(pre[n] < target){
            return 0;
        }

        int sum;
        int result = Integer.MAX_VALUE;
        // 双指针(滑动窗口)
        for(int i=0,j=1; i<=j&&j<=n;){
            sum = pre[j]-pre[i];
            if(sum >= target){
                result = Math.min(result, j-i);
                i++;
            }else{
                j++;
            }
        }

        return result;
    }

    /**
     * 前缀和 + 双指针(滑动窗口) + 二分
     * @param nums
     * @param target
     * @return
     */
    private int solution2(int[] nums, int target){
        int n = nums.length;

        // 前缀和: pre[i]表示前i项的和
        int[] pre = new int[n+1];
        int val;
        for(int i=1; i<=n; i++){
            val = nums[i-1];
            if(val >= target){
                return 1;
            }
            pre[i] = pre[i-1]+val;
        }
        if(pre[n] < target){
            return 0;
        }

        // 二分 <- 前缀数组pre 单调增
        // 快速找到第一个满足条件的位置
        int left = 1;
        int right = n;
        int mid;
        while(left <= right){
            mid = (left+right)/2;
            if(pre[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }

        int sum;
        int result = Integer.MAX_VALUE;
        // 双指针(滑动窗口)
        for(int i=0,j=left; i<=j&&j<=n;){
            sum = pre[j]-pre[i];
            if(sum >= target){
                result = Math.min(result, j-i);
                i++;
            }else{
                j++;
            }
        }

        return result;
    }

    /**
     * 前缀和 + 二分
     * @param nums
     * @param target
     * @return
     */
    private int solution3(int[] nums, int target){
        int n = nums.length;

        // 前缀和: pre[i]表示前i项的和
        int[] pre = new int[n+1];
        int val;
        for(int i=1; i<=n; i++){
            val = nums[i-1];
            if(val >= target){
                return 1;
            }
            pre[i] = pre[i-1]+val;
        }
        if(pre[n] < target){
            return 0;
        }

        // 二分 <- 前缀数组pre 单调增
        int result = Integer.MAX_VALUE;
        for(int i=0; i<n; i++){
            int left = i;
            int right = n;
            int mid;
            while(left <= right){
                mid = (left+right)/2;
                // 关键! left最终指向第一个满足条件(pre[mid]-pre[i] >= target)的位置(从左往右i->n)
                if(pre[mid] < pre[i]+target){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
            if(left <= n){
                result = Math.min(result, left-i);
            }
        }

        return result;
    }

    /**
     * 前缀和 + 二分
     * @param nums
     * @param target
     * @return
     */
    private int solution4(int[] nums, int target){
        int n = nums.length;

        // 前缀和: pre[i]表示前i项的和
        int[] pre = new int[n+1];
        int val;
        for(int i=1; i<=n; i++){
            val = nums[i-1];
            if(val >= target){
                return 1;
            }
            pre[i] = pre[i-1]+val;
        }
        if(pre[n] < target){
            return 0;
        }

        // 二分 <- 子数组长度
        int left = 1;
        int right = n;
        int mid;
        while(left <= right){
            mid = (left+right)/2;
            boolean found = false;
            for(int i=0,j=i+mid; j<=n; i++,j++){
                if(pre[j]-pre[i] >= target){
                    found = true;
                    break;
                }
            }
            if(!found){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }

        // left>n => 不存在满足条件的子数组
        int result = left>n?0:left;

        return result;
    }
}